package its.opt.graphs;

import engine.BoaPopulation;
import engine.individuals.Individual;

public class FrequencyDecisionGraph<T extends Individual<Integer>> extends
		DecisionGraph {

	private BoaPopulation<T> population;
	private int myPosition;

	public FrequencyDecisionGraph(BoaPopulation<T> population, int myPosition) {
		super();

		double f;

		// set the active population and the active bit

		this.population = population;
		this.myPosition = myPosition;

		// allocate the instance index stuff (we don't do this in traditional
		// decision trees)

		getRoot().allocateInstanceIndexEntries(population.getChromosomeSize());

		// the probability in the root is the univariate frequency in the position

		f = univariateFrequency(myPosition);
		setValue(getRoot(), 1.0d - f, 0);
		setValue(getRoot(), f, 1);
	}

	private double univariateFrequency(int k) {

		int N;
		int count;

		// assign helper variables

		N = population.size();

		// compute the frequency of x_k=1

		count = 0;

		for (T individual : population.getIndividuals()) {
			count += individual.get(k);
		}

		// return the resulting frequency

		return (double) count / (double) N;
	}

	/**
	 * @param treeNode
	 * @param left0
	 * @param left1
	 * @param right0
	 * @param right1
	 * @return
	 */
	public int computeSplitFrequencies(LabeledTreeNode treeNode, double[] left0,
			double[] left1, double[] right0, double[] right1) {

		int n;
		long N;
		int label;

		// set helper variables

		N = population.size();
		n = population.getChromosomeSize();

		// reset the counts

		for (label = 0; label < n; label++)
			left0[label] = left1[label] = right0[label] = right1[label] = 0;

		// compute the frequencies

		for (T individual : population.getIndividuals()) {
			LabeledTreeNode node = traverseTreeToLeafBasedOnIndividual(individual);

			if (node == treeNode) {
				if (myPosition >= 0) {
					Integer myGeneValue = individual.get(myPosition);
					for (label = 0; label < n; label++) {
						if (individual.get(label) == 0) {
							if (myGeneValue != 0) {
								left1[label]++;
							} else {
								left0[label]++;
							}
						} else {
							if (myGeneValue != 0) {
								right1[label]++;
							} else {
								right0[label]++;
							}
						}
					}
				} else {
					// FIXME dead code!!!
					for (label = 0; label < n; label++) {
						if (individual.get(label) == 0) {
							left0[label]++;
						} else {
							right0[label]++;
						}
					}
				}
			}
		}

		// divide the values in all leaves by N

		if (myPosition >= 0) {
			for (label = 0; label < n; label++) {
				left0[label] /= N;
				left1[label] /= N;
				right0[label] /= N;
				right1[label] /= N;
			}
		} else {
			// FIXME: dead code!!!
			for (label = 0; label < n; label++) {
				left0[label] /= N;
				right0[label] /= N;
			}
		}

		// get back

		return 0;
	}

	private LabeledTreeNode traverseTreeToLeafBasedOnIndividual(T individual) {
		LabeledTreeNode node = getRoot();

		while (node.type != LabeledTreeNodeType.LEAF) {
			if (individual.get(node.label) == 0) {
				node = node.left;
			} else {
				node = node.right;
			}
		}
		return node;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see its.opt.graphs.DecisionGraph#merge(its.opt.graphs.LabeledTreeNode,
	 *      its.opt.graphs.LabeledTreeNode)
	 */
	@Override
	public int merge(LabeledTreeNode x, LabeledTreeNode y) {
		// increase the value in x by the value in y

		x.distribution[0] += y.distribution[0];
		if (myPosition >= 0)
			x.distribution[1] += y.distribution[1];

		// merge the children of x now

		super.merge(x, y);

		// get back

		return 0;
	}

	@Override
	public int split(LabeledTreeNode node, int label) {
		// split the node x now

		super.split(node, label);

		// allocate instance/frequency related stuff for both children

		node.left.allocateInstanceIndexEntries(population.getChromosomeSize());
		node.right.allocateInstanceIndexEntries(population.getChromosomeSize());

		// it is sufficient to compute frequencies for one of the two new instances,
		// because the second one can be computed by using the value in x (which is
		// the sum of the two), so that's what we're gonna do

		// copy the instance information from the parent to the new children (if
		// any)

		if (node.depth > 0) {
			// memcpy(x.left.parentLabelCoincidenceVector,x.parentLabelCoincidenceVector,population.n);
			// memcpy(x.right.parentLabelCoincidenceVector,x.parentLabelCoincidenceVector,population.n);

			for (int i = 0; i < population.getChromosomeSize(); ++i) {
				node.left.parentLabelCoincidenceVector[i] = node.parentLabelCoincidenceVector[i];
				node.right.parentLabelCoincidenceVector[i] = node.parentLabelCoincidenceVector[i];
			}
		}

		// add the new information to each child

		node.left.parentLabelCoincidenceVector[node.label] = 1;
		node.right.parentLabelCoincidenceVector[node.label] = 1;

		// should recompute frequencies?!!! (well i take care of this elsewhere
		// its faster - but careful about this....

		// get back

		return 0;
	}

	@Override
	public String toString() {
		return recursivePrint(getRoot(), 2).toString();
	}

	private StringBuilder recursivePrint(LabeledTreeNode node, int shift) {
		
		StringBuilder sb = new StringBuilder();
		if (node == null)
		{
			return sb;
		}
		
		for (int i=0; i<shift; i++)
		{
	    sb.append(' ');
		}
		
		if (node.type==LabeledTreeNodeType.LEAF)
		{
	    sb.append("p(x");
	    sb.append(myPosition);
	    sb.append("=1|...)=");
	    sb.append(node.distribution[1]/(node.distribution[0]+node.distribution[1]));
	    sb.append('\n');
		}
		
	  if (node.type==LabeledTreeNodeType.SPLIT)
	  {
	    sb.append(node.label);
	    sb.append('\n');
	  }

	  // print the children sub-trees

	  sb.append(recursivePrint(node.left,shift+2));
	  sb.append(recursivePrint(node.right,shift+2));
	  
		return sb;
	}
}
